package editor.modelo;

import java.awt.geom.Point2D;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JOptionPane;

import editor.modelo.Portal.Clase;
import editor.modelo.Portal.Tipo;

/**
 * Un Floor mantiene toda la información de los espacios que lo contienen, pero
 * a su vez es un espacio con sus propios portales a otros espacios ya que forma
 * parte del grafo de caminos, pero a un nivel de abstraccion mas alto (para
 * achicar los espacios de busqueda.
 * 
 * @author Alejandro
 * 
 */
public class EditorFloor extends Floor {

	public static int ALTURA_NIVEL = 100;

	public EditorFloor() {
		inicializar();
	}

	public String getId() {
		return this.getFloorName().substring(0, 2);
	}

	@Override
	public void inicializar() {
		this.espacios = new ArrayList<Space>();
		this.portales = new ArrayList<Portal>();
		this.landmarks = new ArrayList<LandMark>();
		miInfo = new EditorSpace();
	}

	public void insertPortal(EditorPortal p) {
		this.portales.add(p);
	}

	private void insertSpace(EditorSpace sp) {
		this.validateID(sp);
		this.espacios.add(sp);
	}

	public EditorSpace addNewSpace(float cx, float cy) {
		EditorSpace nuevoEspacio = new EditorSpace(new Point2D.Float(cx, cy),
				true);
		// nuevoEspacio.setCentro(cx, cy);
		nuevoEspacio.nombrePadre = this.getCompleteFloorName();
		this.insertSpace(nuevoEspacio);
		
		return nuevoEspacio;
	}

	public EditorPortal addNewInternalPortal(EditorSpace source,
			Point2D edgeSource, EditorSpace dest, Point2D edgeDest) {
		EditorPortal p = addNewUnlinkedPortal(source, edgeSource);
		p.setTarget(dest);
		p.setPtoTarget(edgeDest);
		return p;
	}

	public EditorPortal addNewUnlinkedPortal(EditorSpace space, Point2D pto) {
		EditorPortal p = new EditorPortal();
		p.setSource(space);
		p.setTarget(null);
		p.setPtoSource(pto);
		p.setPtoTarget(null);
		p.setTipo(Tipo.door);
		p.setClase(Clase.intra_floor);
		p.setBidirectional(true);
		this.portales.add(p);
		return p;
	}

	public EditorPortal addNewExternalPortal(EditorSpace source, Point2D pto1) {
		EditorPortal p = addNewUnlinkedPortal(source, pto1);
		p.setClase(Clase.inter_floor);
		return p;
	}

	public void checkFormat() {
		for (Space s : espacios) {
			this.correctOrientation(((EditorSpace) s));
		}
		// calculateHeights();
	}

	/**
	 * Hay que cambiar el algoritmo. Buscar el punto de mas abajo a la izq y
	 * fijarse sobre esos vectores la orientaci�n.
	 * 
	 * @param s
	 */
	public void correctOrientation(EditorSpace s) {
		Point2D leftBot = getLeftBottom(s);
		Point2D next = s.getNextPoint(leftBot);
		Point2D prev = s.getPrevPoint(leftBot);
		float z = zComponent(prev, leftBot, next);
		//System.out.println(z);
		//System.out.println("el pto era "+ next.getX() + " " + next.getY());
		//next = leftBot;
		
	//	System.out.println(next.getX() + " " + next.getY());
	//	next = prev;
	//	System.out.println(next.getX() + " " + next.getY());
		if (z > 0) {
			System.out.println("invertir " + s.getId()); // + p1 + p2
			invertPoints(s);
		}
	}
		/*int i = 0;
		while (true) {
			Point2D p1 = s.getBorde().get(i);
			Point2D p2 = s.getBorde().get(i + 1);
			Point2D p3 = s.getNextPoint(s.getBorde().get(i + 1));
			float z = zComponent(p1, p2, p3);
			// System.out.println(z);
			if (Math.abs(z) > 1500) {
				if (z > 0) {
					System.out.println("invertir " + s.getId()); // + p1 + p2
					invertPoints(s);
				}
				break;
			}
			i++;
			if (i == s.getBorde().size() - 1)
				break; // error
		}*/

	private Point2D getLeftBottom(EditorSpace s){
		Point2D best = s.getBorde().get(0);
		for(Point2D pto: s.getBorde()){
			if(pto.getX() < best.getX()){
				best = pto;
			}else if(pto.getX() == best.getX() && pto.getY()< best.getY()){
				best = pto;
			}
		}
		return best;
		
	}

	public void move(int offX, int offY) {
		for (Space s : this.espacios) {
			((EditorSpace) s).move(offX, offY);
		}
		for (LandMark lm : this.landmarks) {
			lm.centro.setLocation(lm.centro.getX() + offX, lm.centro.getY()
					+ offY);
		}

	}

	/*
	 * Calcula la altura de todos los spaces segun la altura de las escaleras.
	 * No acepta inconsistencias (el resultado en tal caso es impredecible)
	 */

	private void invertPoints(EditorSpace s) {
		List<Point2D> borde = s.getBorde();
		for (Portal p : this.getPortales()) { // invertir portales
			if (p.source == s)
				p.ptoSource = p.source.getNextPoint(p.ptoSource);
			if (p.target == s)
				p.ptoTarget = p.target.getNextPoint(p.ptoTarget);
		}
		int izq = 0;
		int der = borde.size() - 1;
		Point2D aux;
		while (izq < der) {
			aux = borde.get(der);
			borde.set(der, borde.get(izq));
			borde.set(izq, aux);
			izq++;
			der--;
		}

	}

	private float zComponent(Point2D p1, Point2D p2, Point2D p3) {
		// vectores p3 - p2 y p1- p2
		Point2D u = new Point2D.Double(p3.getX() - p2.getX(), p3.getY()
				- p2.getY());
		Point2D v = new Point2D.Double(p2.getX() - p1.getX(), p2.getY()
				- p1.getY());
		// ux*vy – uy*vx

		return (float) (u.getX() * v.getY() - u.getY() * v.getX());
	}

	public LandMark addNewLandMark(float xMouse, float yMouse, Space parent) {
		LandMark lm = new EditorLandMark();
		lm.setCentro(xMouse, yMouse);
		lm.setParent(parent);
		lm.setTipo(LandMark.Tipo.values()[0]);
		lm.setName("ponme un nombre");
		getLandmarks().add(lm);
		return lm;
	}

	public void validateID(EditorSpace espacio) {
		String id = espacio.getId();
		if (id == null || id.equals("")) {
			espacio.setId(String.valueOf(getFreeSlot()));
		}
		else{
			for (Space sp : this.espacios) {
				if (sp != espacio)
					if (sp.getId().equals(id)) {
						espacio.setId(String.valueOf(getFreeSlot()));
						return;
					}
			}
		}
	}

	private int getFreeSlot() {
		int i = espacios.size()+1;
		boolean b = true;
		while (b) {
			b = false;
			for (Space sp : this.espacios) {
				if (sp.getId().equals(String.valueOf(i))) {
					b = true;
					i++;
					break;
				}
			}
		}
		return i;
	}

}
